---
title: "Internationalization (i18n)"
description: "Add multi-language support to MetaMCP with Next.js locale routing and client-side translations"
---

MetaMCP uses **Next.js locale-based routing** and **client-side translations** to support multiple languages. This guide explains the i18n system and how to add new languages.

## Current Language Support

MetaMCP currently supports:

- **English (en)** - Default language
- **Chinese Simplified (zh)** - Full translation available

The author maintains both languages for translation accuracy, but contributions for additional languages are welcome.

## Project Structure

The internationalization system is organized as follows:

```bash
apps/frontend/
├── app/
│   └── [locale]/                  # Locale-based routing
│       ├── layout.tsx            # Locale layout
│       ├── (sidebar)/            # Sidebar layout group
│       └── ...
├── public/locales/
│   ├── en/                       # English translations
│   │   ├── common.json
│   │   ├── auth.json
│   │   ├── navigation.json
│   │   ├── mcp-servers.json
│   │   ├── namespaces.json
│   │   ├── endpoints.json
│   │   ├── api-keys.json
│   │   ├── settings.json
│   │   ├── search.json
│   │   ├── inspector.json
│   │   ├── logs.json
│   │   └── validation.json
│   └── zh/                       # Chinese translations
│       └── (same structure)
├── lib/
│   └── i18n.ts                  # Client-side i18n utilities
├── hooks/
│   ├── useLocale.ts             # Hook to get current locale
│   └── useTranslations.ts       # Hook for client-side translations
├── components/
│   └── language-switcher.tsx    # Language switching component
└── middleware.ts                # Locale detection and routing
```

## How It Works

### URL Structure

MetaMCP uses locale-based routing:

- **English (default)**: `/mcp-servers`, `/settings`, `/namespaces`
- **Chinese**: `/zh/mcp-servers`, `/zh/settings`, `/zh/namespaces`

### Middleware

The `middleware.ts` file handles:

- **Locale detection** from URL, cookies, and Accept-Language header
- **Automatic redirects** to appropriate locale
- **Authentication checks**

<CodeGroup>
```typescript middleware.ts
import { NextRequest } from 'next/server';
import { getLocale, getLocalizedPath } from '@/lib/i18n';

export function middleware(request: NextRequest) {
  // Detect locale from URL, cookie, or headers
  const locale = getLocale(request);
  
  // Redirect if needed
  if (!request.nextUrl.pathname.startsWith(`/${locale}`)) {
    const localizedPath = getLocalizedPath(request.nextUrl.pathname, locale);
    return Response.redirect(new URL(localizedPath, request.url));
  }
}
```

```typescript lib/i18n.ts
export function getLocalizedPath(path: string, locale: string): string {
  if (locale === 'en') {
    return path; // Default locale doesn't need prefix
  }
  return `/${locale}${path}`;
}

export function detectLocale(request: NextRequest): string {
  // Check URL first, then cookies, then Accept-Language
  // Return detected locale or fallback to 'en'
}
```
</CodeGroup>

## Using Translations

### Client Components

For client-side components, use the `useTranslations` hook:

<CodeGroup>
```tsx Basic Usage
"use client";

import { useTranslations } from "@/hooks/useTranslations";

function ClientComponent() {
  const { t, isLoading, locale } = useTranslations();
  
  if (isLoading) return <div>Loading...</div>;
  
  return (
    <div>
      <h1>{t('common:title')}</h1>
      <button>{t('auth:signIn')}</button>
    </div>
  );
}
```

```tsx With Parameters
// In translation file: "welcome": "Welcome, {{name}}!"
<span>{t('common:welcome', { name: 'John' })}</span>

// With count: "itemCount": "{{count}} items found"
<span>{t('search:itemCount', { count: 42 })}</span>
```

```tsx Conditional Translations
const { t, locale } = useTranslations();

return (
  <div>
    <p>{t('common:currentLanguage')}: {locale}</p>
    {locale === 'zh' && (
      <p>{t('common:chineseSpecificMessage')}</p>
    )}
  </div>
);
```
</CodeGroup>

### Translation Key Format

Use colon-separated namespaces for organization:

```json
{
  "server": {
    "create": "Create Server",
    "edit": "Edit Server",
    "delete": "Delete Server",
    "status": {
      "online": "Online",
      "offline": "Offline",
      "error": "Error"
    },
    "validation": {
      "nameRequired": "Server name is required",
      "commandRequired": "Command is required"
    }
  }
}
```

**Usage**: `t('mcp-servers:server.create')`, `t('mcp-servers:server.status.online')`

## Translation File Organization

### Namespace Structure

Each translation namespace serves a specific purpose:

<AccordionGroup>
  <Accordion icon="globe" title="common.json">
    **Shared UI elements and general terms**
    
    ```json
    {
      "actions": {
        "save": "Save",
        "cancel": "Cancel",
        "delete": "Delete",
        "edit": "Edit",
        "create": "Create",
        "search": "Search"
      },
      "status": {
        "loading": "Loading...",
        "error": "Error",
        "success": "Success"
      },
      "form": {
        "required": "This field is required",
        "invalid": "Invalid input"
      }
    }
    ```
  </Accordion>

  <Accordion icon="lock" title="auth.json">
    **Authentication-related text**
    
    ```json
    {
      "signIn": "Sign In",
      "signOut": "Sign Out",
      "signUp": "Sign Up",
      "email": "Email",
      "password": "Password",
      "forgotPassword": "Forgot Password?",
      "createAccount": "Create Account",
      "loginWithOIDC": "Login with OIDC"
    }
    ```
  </Accordion>

  <Accordion icon="navigation" title="navigation.json">
    **Menu items and navigation text**
    
    ```json
    {
      "dashboard": "Dashboard",
      "mcpServers": "MCP Servers",
      "namespaces": "Namespaces",
      "endpoints": "Endpoints",
      "apiKeys": "API Keys",
      "settings": "Settings",
      "inspector": "MCP Inspector",
      "logs": "Live Logs"
    }
    ```
  </Accordion>

  <Accordion icon="server" title="mcp-servers.json">
    **MCP server-specific translations**
    
    ```json
    {
      "server": {
        "create": "Create Server",
        "edit": "Edit Server",
        "name": "Server Name",
        "type": "Server Type",
        "command": "Command",
        "args": "Arguments",
        "env": "Environment Variables"
      },
      "types": {
        "stdio": "STDIO",
        "http": "HTTP",
        "websocket": "WebSocket"
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Best Practices for Translation Keys

<Card title="Translation Key Guidelines" icon="key">
- **Use descriptive, hierarchical keys**: `server.validation.nameRequired`
- **Use camelCase for consistency**: `signIn`, `mcpServers`
- **Group related translations**: All server-related terms under `server`
- **Keep context clear**: `auth:signIn` vs `form:signIn` if different
- **Use interpolation for dynamic content**: `"welcome": "Welcome, {{name}}!"`
</Card>

## Adding New Languages

### Step 1: Create Translation Files

1. **Create language directory** in `public/locales/`:
   ```bash
   mkdir -p public/locales/es  # For Spanish
   ```

2. **Copy English files** as templates:
   ```bash
   cp -r public/locales/en/* public/locales/es/
   ```

3. **Translate the content** in each JSON file:
   ```json
   // public/locales/es/common.json
   {
     "actions": {
       "save": "Guardar",
       "cancel": "Cancelar",
       "delete": "Eliminar",
       "edit": "Editar",
       "create": "Crear"
     }
   }
   ```

### Step 2: Update Configuration

Add the new locale to your i18n configuration:

<CodeGroup>
```typescript lib/i18n.ts
export const SUPPORTED_LOCALES = ['en', 'zh', 'es'] as const;
export type Locale = typeof SUPPORTED_LOCALES[number];

export const LOCALE_NAMES: Record<Locale, string> = {
  en: 'English',
  zh: '中文',
  es: 'Español'
};
```

```typescript middleware.ts
import { SUPPORTED_LOCALES } from '@/lib/i18n';

export function middleware(request: NextRequest) {
  // Update locale detection to include new language
  const supportedLocales = SUPPORTED_LOCALES;
  // ... rest of middleware logic
}
```
</CodeGroup>

### Step 3: Update Language Switcher

The language switcher will automatically include new languages:

```tsx
// components/language-switcher.tsx
import { LOCALE_NAMES, SUPPORTED_LOCALES } from '@/lib/i18n';

export function LanguageSwitcher() {
  return (
    <select>
      {SUPPORTED_LOCALES.map(locale => (
        <option key={locale} value={locale}>
          {LOCALE_NAMES[locale]}
        </option>
      ))}
    </select>
  );
}
```

### Step 4: Test the Implementation

1. **Add test content** in the new language
2. **Navigate to** `/{locale}/` URLs (e.g., `/es/mcp-servers`)
3. **Verify translations** appear correctly
4. **Test language switching** functionality
5. **Check fallbacks** work for missing translations

## Translation Workflow

### For New Features

When adding new features to MetaMCP:

1. **Add English translations first** in appropriate namespace
2. **Use descriptive keys** that make sense in context
3. **Test with English** to ensure keys work correctly
4. **Add other languages** (or mark for translation)
5. **Test all languages** before deployment

### For Contributors

<AccordionGroup>
  <Accordion icon="translate" title="Translation Contributors">
    **To contribute translations:**
    
    1. Fork the repository
    2. Create new language files or update existing ones
    3. Follow the existing key structure
    4. Test your translations locally
    5. Submit a Pull Request with your changes
    
    **Tips:**
    - Keep translations concise but clear
    - Maintain consistent terminology
    - Consider cultural context, not just literal translation
    - Test with longer text to ensure UI still works
  </Accordion>

  <Accordion icon="robot" title="AI-Assisted Translation">
    **Using AI tools like Cursor/Claude:**
    
    ```prompt
    Translate this English JSON file to Spanish, maintaining the same structure and keys:
    
    {
      "server": {
        "create": "Create Server",
        "edit": "Edit Server"
      }
    }
    
    Keep technical terms like "MCP" and "API" unchanged.
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

### Common Issues

<AccordionGroup>
  <Accordion icon="warning" title="Missing Translations">
    **When translations don't appear:**
    
    1. Check the translation key exists in the JSON file
    2. Verify the namespace is correct (`common:save` vs `auth:save`)
    3. Ensure the locale file exists and is valid JSON
    4. Check browser console for missing key warnings
    5. Verify the component is using `useTranslations` correctly
  </Accordion>

  <Accordion icon="bug" title="Hydration Errors">
    **Server/client translation mismatches:**
    
    1. Ensure consistent locale detection between server and client
    2. Use the `isLoading` state from `useTranslations`
    3. Avoid rendering translations during SSR if locale might change
    4. Test with JavaScript disabled to check SSR behavior
  </Accordion>

  <Accordion icon="globe" title="Locale Routing Issues">
    **URL routing problems:**
    
    1. Check middleware configuration for new locales
    2. Verify `getLocalizedPath` function handles new languages
    3. Test direct navigation to localized URLs
    4. Ensure fallback behavior works correctly
  </Accordion>
</AccordionGroup>

### Debugging Tools

<CodeGroup>
```bash Development Debugging
# Check for missing translation keys
grep -r "t('" apps/frontend/app --include="*.tsx" | \
  grep -v "useTranslations"

# Validate JSON files
for file in public/locales/*/*.json; do
  echo "Checking $file"
  cat "$file" | jq . > /dev/null
done
```

```typescript Debug Component
"use client";

import { useTranslations } from "@/hooks/useTranslations";

export function TranslationDebugger() {
  const { t, locale, isLoading } = useTranslations();
  
  return (
    <div className="debug-panel">
      <p>Current locale: {locale}</p>
      <p>Is loading: {isLoading.toString()}</p>
      <p>Test translation: {t('common:save')}</p>
    </div>
  );
}
```
</CodeGroup>

## Future Enhancements

### Planned Features

- **RTL language support** for Arabic, Hebrew
- **Date/time localization** with proper formatting
- **Number formatting** based on locale
- **Currency formatting** for pricing features
- **Pluralization rules** for complex language requirements

### Contributing Guidelines

<Card title="i18n Contributing Guidelines" icon="checklist">
- 📝 **Add English first**: Always start with English translations
- 🔍 **Test thoroughly**: Verify all locales work correctly
- 📊 **Use consistent terminology**: Maintain glossary for technical terms
- 🌍 **Consider context**: Adapt to cultural differences, not just language
- 📱 **Test UI impact**: Ensure longer translations don't break layout
- 🤝 **Collaborate**: Work with native speakers when possible
</Card>

## Next Steps

<CardGroup cols={2}>
  <Card title="Contributing Guide" icon="handshake" href="/en/development/contributing">
    Learn how to contribute to MetaMCP development
  </Card>
  
  <Card title="Frontend Development" icon="code" href="/en/development">
    Understand the frontend architecture and development setup
  </Card>
  
  <Card title="Component Development" icon="component" href="/en/development#frontend-development">
    Learn about UI component development with i18n
  </Card>
  
  <Card title="Testing Guide" icon="test" href="/en/development#testing">
    Test your internationalization changes
  </Card>
</CardGroup> 